﻿using System;
using System.Collections.Generic;
using System.Linq;

using DDDSample.Domain.Model.Carrier;
using DDDSample.Domain.Model.Handling;
using DDDSample.Domain.Model.Locations;

namespace DDDSample.Domain.Model.Cargos
{
	/// <summary>
	/// The delivery history of a cargo.
	/// 
	/// This is a value object.
	/// </summary>
	public class DeliveryHistory : ValueObject<DeliveryHistory>
	{
		private HashSet<HandlingEvent> events;

		public static readonly DeliveryHistory EMPTY_DELIVERY_HISTORY = new DeliveryHistory(new List<HandlingEvent>());

		public DeliveryHistory(ICollection<HandlingEvent> events)
		{
			this.events = new HashSet<HandlingEvent>(events);
		}

		/// <summary>
		/// An <b>unmodifiable</b> list of handling events, ordered by the time the events occured.
		/// </summary>
		public IList<HandlingEvent> EventsOrderedByCompletionTime
		{
			get
			{
				List<HandlingEvent> eventList = new List<HandlingEvent>(events);
				eventList.Sort(HandlingEvent.BY_COMPLETION_TIME_COMPARER);
				return eventList.AsReadOnly();
			}
		}

		/// <summary>
		/// The last event of the delivery history, or null is history is empty.
		/// </summary>
		public HandlingEvent LastEvent
		{
			get
			{
				if (events.Count == 0)
				{
					return null;
				}
				else
				{
					IList<HandlingEvent> orderedEvents = EventsOrderedByCompletionTime;
					return orderedEvents[orderedEvents.Count - 1];
				}
			}
		}

		public StatusCode Status
		{
			get
			{
				if (LastEvent == null)
					return StatusCode.NotReceived;

				HandlingEvent.EventType type = LastEvent.Type;

				switch (type)
				{
					case HandlingEvent.EventType.Load:
						return StatusCode.OnboardCarrier;
						break;
					case HandlingEvent.EventType.Unload:
					case HandlingEvent.EventType.Receive:
					case HandlingEvent.EventType.Customs:
						return StatusCode.InPort;
						break;
					case HandlingEvent.EventType.Claim:
						return StatusCode.Claimed;
						break;
					default:
						return (StatusCode)(-1);
						break;
				}
			}
		}

		public Location CurrentLocation
		{
			get
			{
				if (Status == StatusCode.InPort)
				{
					return LastEvent.Location;
				}
				else
				{
					return null;
				}
			}
		}

		public CarrierMovement CurrentCarrierMovement
		{
			get
			{
				if (Status == StatusCode.OnboardCarrier)
				{
					return LastEvent.CarrierMovement;
				}
				else
				{
					return null;
				}
			}
		}

		public override bool SameValueAs(DeliveryHistory other)
		{
			return other != null && events.SetEquals(other.events);
		}

		public override bool Equals(object obj)
		{
			if (this == obj) return true;
			if (obj == null || GetType() != obj.GetType()) return false;

			DeliveryHistory other = (DeliveryHistory)obj;

			return SameValueAs(other);
		}

		public override int GetHashCode()
		{
			if (events.Count == 0) return 0;

			if (events.Count == 1) return events.First().GetHashCode();

			IList<HandlingEvent> eventList = EventsOrderedByCompletionTime;

			int hashCode = 37 + eventList[0].GetHashCode();
			for (int i = 2; i < eventList.Count; i++)
				hashCode = hashCode * 17 + eventList[i].GetHashCode();

			return hashCode;
		}

		private DeliveryHistory()
		{
			// Needed by Hibernate
		}
	}
}
